﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Xml;
using Mbs.Configuration;

namespace Mbs.Modularity.AddIns
{
    [Serializable]
    class ExtensionPoint : IExtensionPoint, ICloneable
    {
        public string Value { get; private set; }
        public bool IsAsyn { get; private set; }

        public ExtensionPoint(string value, bool isAsyn)
        {
            Value = value;
            IsAsyn = isAsyn;
        }

        object ICloneable.Clone()
        {
            return Clone();
        }

        public IExtensionPoint Clone()
        {
            return base.MemberwiseClone() as ExtensionPoint;
        }

    }

    [Serializable]
    internal class Runtime : MarshalByRefObject, IRuntime
    {
        
        public string AddInName { get; private set; }
        private HashSet<string> imports;
        public IEnumerable<string> Imports { get { return imports.ToArray(); } }

        public XmlNode Services { get; internal set; }

        private HashSet<IExtensionPoint> extensionPoints;
        public IEnumerable<IExtensionPoint> ExtensionPoints { get { return extensionPoints; } }

        public Runtime()
        {
            imports = new HashSet<string>();
            extensionPoints = new HashSet<IExtensionPoint>();
        }

        public void AddImport(string import)
        {
            imports.Add(import);
        }

        public void AddExtensionPoint(IExtensionPoint extensionPoint)
        {
            extensionPoints.Add(extensionPoint);
        }

        public IRuntime Clone()
        {
            var o = new Runtime();
            o.AddInName = AddInName;

            if (imports.Count > 0)
                foreach (var item in imports)
                    o.AddImport(item);
            if (Services != null && Services.ChildNodes.Count > 0)
                o.Services = Services.Clone();
            if (extensionPoints.Count > 0)
                foreach (var item in extensionPoints)
                    o.extensionPoints.Add(item.Clone());
            return o;
        }


        public static IRuntime Parse(XmlNode xmlNode,string addInName)
        {

            var runtime = new Runtime();
            runtime.AddInName = addInName;

            if (xmlNode.ChildNodes.Count < 1)
                return runtime;

            foreach (XmlNode node in xmlNode.ChildNodes)
            {
                switch (node.Name.ToLower())
                {
                    case "imports":
                        runtime.ParseImports(node.ChildNodes);
                        break;
                    case "services":
                        runtime.Services = node.Clone();
                        break;
                    case "extensionpoints":
                        runtime.ParseExtensionPoints(new XmlConfiguration(node));
                        break;
                }
            }

            return runtime;
        }

        private void ParseImports(XmlNodeList nodes)
        {
            if (nodes.Count < 1)
                return;
            foreach (XmlNode node in nodes)
                if (node.Name.ToLower() == "import")
                {
                    var import = node.InnerText;
                    if (string.IsNullOrEmpty(import))
                        throw new ConfigurationErrorsException("import element' value is null in addin :" + AddInName);
                    else
                        if (File.Exists(import))
                            AddImport(import);
                        else
                        {
                            var systemImport = MbsEnvironment.SDK_Path + import;
                            if (File.Exists(systemImport))
                                AddImport(systemImport);
                            else
                            {
                                import = "AddIns/" + import;
                                if (File.Exists(import))
                                    AddImport(import);
                                else
                                    throw new ConfigurationErrorsException("import element's value :" + node.InnerText + " does not exists in file system in addin :" + AddInName);
                            }
                        }


                }


        }


        private void ParseExtensionPoints(XmlConfiguration node)
        {
            foreach (var subNode in node.Nodes)
            {
                switch (subNode.Name.ToLower())
                {
                    case "item":
                        if (!string.IsNullOrEmpty(subNode.Value))
                            AddExtensionPoint(new ExtensionPoint(subNode.Value, subNode.Attributes.Get<bool>("IsAsyn", true)));
                        break;
                }
            }
        }
    }
}
